home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 216_01 / sz.c < prev   
Encoding:
C/C++ Source or Header  |  1980-01-01  |  28.1 KB  |  1,342 lines

  1. #define VERSION "sz 1.22 01-01-87"
  2. #define PUBDIR "/usr/spool/uucppublic"
  3.  
  4. /*% cc -O -K -i -DCRCTABLE -DREADCHECK sz.c -lx -o sz; size sz
  5.  
  6.  * sz.c By Chuck Forsberg
  7.  *
  8.  *    cc -O sz.c -o sz        USG (SYS III/V) Unix
  9.  *    cc -O -DSVR2 sz.c -o sz        Sys V Release 2 with non-blocking input
  10.  *                    Define to allow reverse channel checking
  11.  *     cc -O -DV7  sz.c -o sz        Unix Version 7, 2.8 - 4.3 BSD
  12.  *
  13.  *    ln sz sb            **** All versions ****
  14.  *
  15.  *        define CRCTABLE to use table driven CRC
  16.  *
  17.  *  ******* Some systems (Venix, Coherent, Regulus) do not *******
  18.  *  ******* support tty raw mode read(2) identically to    *******
  19.  *  ******* Unix. ONEREAD must be defined to force one     *******
  20.  *  ******* character reads for these systems.           *******
  21.  *
  22.  * A program for Unix to send files and commands to computers running
  23.  *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting Y/XMODEM.
  24.  *
  25.  *  Sz uses buffered I/O to greatly reduce CPU time compared to UMODEM.
  26.  *
  27.  *  USG UNIX (3.0) ioctl conventions courtesy Jeff Martin
  28.  */
  29.  
  30.  
  31. char *substr(), *getenv();
  32.  
  33. #define LOGFILE "/tmp/szlog"
  34. #define zperr vfile
  35.  
  36. #include <stdio.h>
  37. #include <signal.h>
  38. #include <setjmp.h>
  39. #include <ctype.h>
  40.  
  41. #define PATHLEN 256
  42. #define OK 0
  43. #define FALSE 0
  44. #define TRUE 1
  45. #define ERROR (-1)
  46.  
  47. #define HOWMANY 2
  48. int Zmodem=0;        /* ZMODEM protocol requested */
  49. unsigned Baudrate;
  50. int Fromcu = 0;        /* Were called from cu or yam */
  51. #include "rbsb.c"    /* most of the system dependent stuff here */
  52.  
  53. /*
  54.  * Attention string to be executed by receiver to interrupt streaming data
  55.  *  when an error is detected.  A pause (0336) may be needed before the
  56.  *  ^C (03) or after it.
  57.  */
  58. #ifdef READCHECK
  59. char Myattn[] = { 0 };
  60. #else
  61. #ifdef USG
  62. char Myattn[] = { 03, 0336, 0 };
  63. #else
  64. char Myattn[] = { 0 };
  65. #endif
  66. #endif
  67.  
  68. FILE *in;
  69.  
  70. /* Ward Christensen / CP/M parameters - Don't change these! */
  71. #define ENQ 005
  72. #define CAN ('X'&037)
  73. #define XOFF ('s'&037)
  74. #define XON ('q'&037)
  75. #define SOH 1
  76. #define STX 2
  77. #define EOT 4
  78. #define ACK 6
  79. #define NAK 025
  80. #define CPMEOF 032
  81. #define WANTCRC 0103    /* send C not NAK to get crc not checksum */
  82. #define WANTG 0107    /* Send G not NAK to get nonstop batch xmsn */
  83. #define TIMEOUT (-2)
  84. #define RCDO (-3)
  85. #define RETRYMAX 10
  86. #define SECSIZ 128    /* cp/m's Magic Number record size */
  87. #define KSIZE 1024
  88.  
  89. char Lastrx;
  90. char Crcflg;
  91. int Wcsmask=0377;
  92. int Verbose=0;
  93. int Modem=0;        /* MODEM - don't send pathnames */
  94. int Restricted=0;    /* restricted; no /.. or ../ in filenames */
  95. int Quiet=0;        /* overrides logic that would otherwise set verbose */
  96. int Ascii=0;        /* Add CR's for brain damaged programs */
  97. int Fullname=0;        /* transmit full pathname */
  98. int Unlinkafter=0;    /* Unlink file after it is sent */
  99. int Dottoslash=0;    /* Change foo.bar.baz to foo/bar/baz */
  100. int firstsec;
  101. int errcnt=0;        /* number of files unreadable */
  102. int blklen=SECSIZ;        /* length of transmitted records */
  103. int Optiong;        /* Let it rip no wait for sector ACK's */
  104. int Noeofseen;
  105. int Totsecs;        /* total number of sectors this file */
  106. char txbuf[KSIZE];
  107. int Filcnt=0;        /* count of number of files opened */
  108. int Lfseen=0;
  109. unsigned Rxbuflen = 16384;    /* Receiver's max buffer length */
  110. int Tframlen = 0;    /* Override for tx frame length */
  111. int blkopt=0;        /* Override value for zmodem blklen */
  112. int Rxflags = 0;
  113. char Lzconv;    /* Local ZMODEM file conversion request */
  114. char Lzmanag;    /* Local ZMODEM file management request */
  115. char Lztrans;
  116. char zconv;        /* ZMODEM file conversion request */
  117. char zmanag;        /* ZMODEM file management request */
  118. char ztrans;        /* ZMODEM file transport request */
  119. int Command;        /* Send a command, then exit. */
  120. char *Cmdstr;        /* Pointer to the command string */
  121. int Cmdtries = 11;
  122. int Cmdack1;        /* Rx ACKs command, then do it */
  123. int Exitcode;
  124. int Testattn;        /* Force receiver to send Attn, etc with qbf. */
  125. char *qbf="The quick brown fox jumped over the lazy dog's back 1234567890\r\n";
  126. long Lastread;        /* Beginning offset of last buffer read */
  127. int Lastc;        /* Count of last buffer read or -1 */
  128. int Dontread;        /* Don't read the buffer, it's still there */
  129.  
  130. jmp_buf tohere;        /* For the interrupt on RX timeout */
  131. jmp_buf intrjmp;    /* For the interrupt on RX CAN */
  132.  
  133. /* called by signal interrupt or terminate to clean things up */
  134. bibi(n)
  135. {
  136.     canit(); fflush(stdout); mode(0);
  137.     fprintf(stderr, "sz: caught signal %d; exiting\n", n);
  138.     if (n == SIGQUIT)
  139.         abort();
  140.     exit(128+n);
  141. }
  142. /* Called when Zmodem gets an interrupt (^X) */
  143. onintr()
  144. {
  145.     signal(SIGINT, SIG_IGN);
  146.     longjmp(intrjmp, -1);
  147. }
  148.  
  149.  
  150. #define sendline(c) putchar(c & Wcsmask)
  151.  
  152. #define xsendline(c) putchar(c)
  153.  
  154. flushmo()
  155. {
  156.     fflush(stdout);
  157. }
  158.  
  159. #define ZKER
  160. int Zctlesc;    /* Encode control characters */
  161. int Nozmodem = 0;    /* If invoked as "sb" */
  162. char *Progname = "sz";
  163. #include "zm.c"
  164.  
  165.  
  166. main(argc, argv)
  167. char *argv[];
  168. {
  169.     register char *cp;
  170.     register npats;
  171.     int agcnt; char **agcv;
  172.     char **patts;
  173.     static char xXbuf[BUFSIZ];
  174.  
  175.     if ((cp = getenv("ZNULLS")) && *cp)
  176.         Znulls = atoi(cp);
  177.     if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
  178.         Restricted=TRUE;
  179.     chkinvok(argv[0]);
  180.  
  181.     Rxtimeout = 600;
  182.     npats=0;
  183.     if (argc<2)
  184.         usage();
  185.     setbuf(stdout, xXbuf);        
  186.     while (--argc) {
  187.         cp = *++argv;
  188.         if (*cp++ == '-' && *cp) {
  189.             while ( *cp) {
  190.                 switch(*cp++) {
  191.                 case '+':
  192.                     Lzmanag = ZMAPND; break;
  193.                 case '1':
  194.                     iofd = 1; break;
  195. #ifdef CSTOPB
  196.                 case '2':
  197.                     Twostop = TRUE; break;
  198. #endif
  199.                 case '7':
  200.                     Wcsmask=0177; break;
  201.                 case 'a':
  202.                     Lzconv = ZCNL;
  203.                     Ascii = TRUE; break;
  204.                 case 'b':
  205.                     Lzconv = ZCBIN; break;
  206.                 case 'C':
  207.                     if (--argc < 1) {
  208.                         usage();
  209.                     }
  210.                     Cmdtries = atoi(*++argv);
  211.                     break;
  212.                 case 'i':
  213.                     Cmdack1 = ZCACK1;
  214.                     /* **** FALL THROUGH TO **** */
  215.                 case 'c':
  216.                     if (--argc != 1) {
  217.                         usage();
  218.                     }
  219.                     Command = TRUE;
  220.                     Cmdstr = *++argv;
  221.                     break;
  222.                 case 'd':
  223.                     ++Dottoslash;
  224.                     /* **** FALL THROUGH TO **** */
  225.                 case 'f':
  226.                     Fullname=TRUE; break;
  227.                 case 'E':
  228.                     Zctlesc = -1; break;
  229.                 case 'e':
  230.                     Zctlesc = 1; break;
  231.                 case 'k':
  232.                     blklen=KSIZE; break;
  233.                 case 'L':
  234.                     if (--argc < 1) {
  235.                         usage();
  236.                     }
  237.                     blkopt = atoi(*++argv);
  238.                     if (blkopt<24 || blkopt>1024)
  239.                         usage();
  240.                     break;
  241.                 case 'l':
  242.                     if (--argc < 1) {
  243.                         usage();
  244.                     }
  245.                     Tframlen = atoi(*++argv);
  246.                     if (Tframlen<32 || Tframlen>1024)
  247.                         usage();
  248.                     break;
  249.                 case 'N':
  250.                     Lzmanag = ZMDIFF;  break;
  251.                 case 'n':
  252.                     Lzmanag = ZMNEW;  break;
  253.                 case 'p':
  254.                     Lzmanag = ZMPROT;  break;
  255.                 case 'r':
  256.                     Lzconv = ZCRESUM;
  257.                 case 'q':
  258.                     Quiet=TRUE; Verbose=0; break;
  259.                 case 't':
  260.                     if (--argc < 1) {
  261.                         usage();
  262.                     }
  263.                     Rxtimeout = atoi(*++argv);
  264.                     if (Rxtimeout<10 || Rxtimeout>1000)
  265.                         usage();
  266.                     break;
  267.                 case 'T':
  268.                     Testattn = TRUE; break;
  269.                 case 'u':
  270.                     ++Unlinkafter; break;
  271.                 case 'v':
  272.                     ++Verbose; break;
  273.                 case 'X':
  274.                     ++Modem; break;
  275.                 case 'y':
  276.                     Lzmanag = ZMCLOB; break;
  277.                 default:
  278.                     usage();
  279.                 }
  280.             }
  281.         }
  282.         else if ( !npats && argc>0) {
  283.             if (argv[0][0]) {
  284.                 npats=argc;
  285.                 patts=argv;
  286.                 if ( !strcmp(*patts, "-"))
  287.                     iofd = 1;
  288.             }
  289.         }
  290.     }
  291.     if (npats < 1 && !Command) 
  292.         usage();
  293.     if (Verbose) {
  294.         if (freopen(LOGFILE, "a", stderr)==NULL) {
  295.             printf("Can't open log file %s\n",LOGFILE);
  296.             exit(0200);
  297.         }
  298.         setbuf(stderr, NULL);
  299.     }
  300.     if ((Fromcu=from_cu()) && !Quiet) {
  301.         if (Verbose == 0)
  302.             Verbose = 2;
  303.     }
  304.  
  305.     mode(1);
  306.  
  307.     if (signal(SIGINT, bibi) == SIG_IGN) {
  308.         signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
  309.     } else {
  310.         signal(SIGINT, bibi); signal(SIGKILL, bibi);
  311.     }
  312.     if ( !Fromcu)
  313.         signal(SIGQUIT, SIG_IGN);
  314.  
  315.     if ( !Modem) {
  316.         if (!Nozmodem) {
  317.             printf("rz\r");  fflush(stdout);
  318.         }
  319.         if (!Command && !Quiet && Verbose != 1) {
  320.             fprintf(stderr, "%s: %d file%s requested:\r\n",
  321.              Progname, npats, npats>1?"s":"");
  322.             for ( agcnt=npats, agcv=patts; --agcnt>=0; ) {
  323.                 fprintf(stderr, "%s ", *agcv++);
  324.             }
  325.             fprintf(stderr, "\r\n");
  326.             printf("\r\n\bSending in Batch Mode\r\n");
  327.         }
  328.         if (!Nozmodem) {
  329.             stohdr(0L);
  330.             if (Command)
  331.                 Txhdr[ZF0] = ZCOMMAND;
  332.             zshhdr(ZRQINIT, Txhdr);
  333.         }
  334.     }
  335.     fflush(stdout);
  336.  
  337.     if (Command) {
  338.         if (getzrxinit()) {
  339.             Exitcode=0200; canit();
  340.         }
  341.         else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
  342.             Exitcode=0200; canit();
  343.         }
  344.     } else if (wcsend(npats, patts)==ERROR) {
  345.         Exitcode=0200;
  346.         canit();
  347.     }
  348.     fflush(stdout);
  349.     mode(0);
  350.     exit((errcnt != 0) | Exitcode);
  351.     /*NOTREACHED*/
  352. }
  353.  
  354. wcsend(argc, argp)
  355. char *argp[];
  356. {
  357.     register n;
  358.  
  359.     Crcflg=FALSE;
  360.     firstsec=TRUE;
  361.     for (n=0; n<argc; ++n) {
  362.         Totsecs = 0;
  363.         if (wcs(argp[n])==ERROR)
  364.             return ERROR;
  365.     }
  366.     Totsecs = 0;
  367.     if (Filcnt==0) {    /* bitch if we couldn't open ANY files */
  368.         if (1) {
  369.             Command = TRUE;
  370.             Cmdstr = "echo \"sz: Can't open any requested files\"";
  371.             if (getnak()) {
  372.                 Exitcode=0200; canit();
  373.             }
  374.             if (!Zmodem)
  375.                 canit();
  376.             else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
  377.                 Exitcode=0200; canit();
  378.             }
  379.             Exitcode = 1; return OK;
  380.         }
  381.         canit();
  382.         fprintf(stderr,"\r\nCan't open any requested files.\r\n");
  383.         return ERROR;
  384.     }
  385.     if (Zmodem)
  386.         saybibi();
  387.     else
  388.         wctxpn("");
  389.     return OK;
  390. }
  391.  
  392. wcs(oname)
  393. char *oname;
  394. {
  395.     register c;
  396.     register char *p;
  397.     struct stat f;
  398.     char name[PATHLEN];
  399.  
  400.     strcpy(name, oname);
  401.  
  402.     if (Restricted) {
  403.         /* restrict pathnames to current tree or uucppublic */
  404.         if ( substr(name, "../")
  405.          || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
  406.             canit();
  407.             fprintf(stderr,"\r\nsz:\tSecurity Violation\r\n");
  408.             return ERROR;
  409.         }
  410.     }
  411.  
  412.     if ( !strcmp(oname, "-")) {
  413.         if ((p = getenv("ONAME")) && *p)
  414.             strcpy(name, p);
  415.         else
  416.             sprintf(name, "s%d.sz", getpid());
  417.         in = stdin;
  418.     }
  419.     else if ((in=fopen(oname, "r"))==NULL) {
  420.         ++errcnt;
  421.         return OK;    /* pass over it, there may be others */
  422.     }
  423.     ++Noeofseen;  Lastread = 0;  Lastc = -1; Dontread = FALSE;
  424.     /* Check for directory or block special files */
  425.     fstat(fileno(in), &f);
  426.     c = f.st_mode & S_IFMT;
  427.     if (c == S_IFDIR || c == S_IFBLK) {
  428.         fclose(in);
  429.         return OK;
  430.     }
  431.  
  432.     ++Filcnt;
  433.     switch (wctxpn(name)) {
  434.     case ERROR:
  435.         return ERROR;
  436.     case ZSKIP:
  437.         return OK;
  438.     }
  439.     if (!Zmodem && wctx()==ERROR)
  440.         return ERROR;
  441.     if (Unlinkafter)
  442.         unlink(oname);
  443.     return 0;
  444. }
  445.  
  446. /*
  447.  * generate and transmit pathname block consisting of
  448.  *  pathname (null terminated),
  449.  *  file length, mode time and file mode in octal
  450.  *  as provided by the Unix fstat call.
  451.  *  N.B.: modifies the passed name, may extend it!
  452.  */
  453. wctxpn(name)
  454. char *name;
  455. {
  456.     register char *p, *q;
  457.     char name2[PATHLEN];
  458.     struct stat f;
  459.  
  460.     if (Modem) {
  461.         if ((in!=stdin) && *name && fstat(fileno(in), &f)!= -1) {
  462.             fprintf(stderr, "Sending %s, %ld blocks: ",
  463.               name, f.st_size>>7);
  464.         }
  465.         fprintf(stderr, "Give your local XMODEM receive command now.\r\n");
  466.         return OK;
  467.     }
  468.     logent("\r\nAwaiting pathname nak for %s\r\n", *name?name:"<END>");
  469.     if ( !Zmodem)
  470.         if (getnak())
  471.             return ERROR;
  472.  
  473.     q = (char *) 0;
  474.     if (Dottoslash) {        /* change . to . */
  475.         for (p=name; *p; ++p) {
  476.             if (*p == '/')
  477.                 q = p;
  478.             else if (*p == '.')
  479.                 *(q=p) = '/';
  480.         }
  481.         if (q && strlen(++q) > 8) {    /* If name>8 chars */
  482.             q += 8;            /*   make it .ext */
  483.             strcpy(name2, q);    /* save excess of name */
  484.             *q = '.';
  485.             strcpy(++q, name2);    /* add it back */
  486.         }
  487.     }
  488.  
  489.     for (p=name, q=txbuf ; *p; )
  490.         if ((*q++ = *p++) == '/' && !Fullname)
  491.             q = txbuf;
  492.     *q++ = 0;
  493.     p=q;
  494.     while (q < (txbuf + KSIZE))
  495.         *q++ = 0;
  496.     if (!Ascii && (in!=stdin) && *name && fstat(fileno(in), &f)!= -1)
  497.         sprintf(p, "%lu %lo %o", f.st_size, f.st_mtime, f.st_mode);
  498.     /* force 1k blocks if name won't fit in 128 byte block */
  499.     if (txbuf[125])
  500.         blklen=KSIZE;
  501.     else {        /* A little goodie for IMP/KMD */
  502.         if (Zmodem)
  503.             blklen = SECSIZ;
  504.         txbuf[127] = (f.st_size + 127) >>7;
  505.         txbuf[126] = (f.st_size + 127) >>15;
  506.     }
  507.     if (Zmodem)
  508.         return zsendfile(txbuf, 1+strlen(p)+(p-txbuf));
  509.     if (wcputsec(txbuf, 0, SECSIZ)==ERROR)
  510.         return ERROR;
  511.     return OK;
  512. }
  513.  
  514. getnak()
  515. {
  516.     register firstch;
  517.  
  518.     Lastrx = 0;
  519.     for (;;) {
  520.         switch (firstch = readock(800,1)) {
  521.         case ZPAD:
  522.             if (getzrxinit())
  523.                 return ERROR;
  524.             Ascii = 0;
  525.             return FALSE;
  526.         case TIMEOUT:
  527.             logent("Timeout on pathname\n");
  528.             return TRUE;
  529.         case WANTG:
  530. #ifdef USG
  531.             mode(2);    /* Set cbreak, XON/XOFF, etc. */
  532. #endif
  533.             Optiong = TRUE;
  534.             blklen=KSIZE;
  535.         case WANTCRC:
  536.             Crcflg = TRUE;
  537.         case NAK:
  538.             return FALSE;
  539.         case CAN:
  540.             if ((firstch = readock(20,1)) == CAN && Lastrx == CAN)
  541.                 return TRUE;
  542.         default:
  543.             break;
  544.         }
  545.         Lastrx = firstch;
  546.     }
  547. }
  548.  
  549.  
  550. wctx()
  551. {
  552.     register int sectnum, attempts, firstch;
  553.  
  554.     firstsec=TRUE;
  555.  
  556.     while ((firstch=readock(Rxtimeout, 2))!=NAK && firstch != WANTCRC
  557.       && firstch != WANTG && firstch!=TIMEOUT && firstch!=CAN)
  558.         ;
  559.     if (firstch==CAN) {
  560.         logent("Receiver CANcelled\n");
  561.         return ERROR;
  562.     }
  563.     if (firstch==WANTCRC)
  564.         Crcflg=TRUE;
  565.     if (firstch==WANTG)
  566.         Crcflg=TRUE;
  567.     sectnum=1;
  568.     while (filbuf(txbuf, blklen)) {
  569.         if (wcputsec(txbuf, sectnum, blklen)==ERROR) {
  570.             return ERROR;
  571.         } else
  572.             sectnum++;
  573.     }
  574.     if (Verbose>1)
  575.         fprintf(stderr, " Closing ");
  576.     fclose(in);
  577.     attempts=0;
  578.     do {
  579.         logent(" EOT ");
  580.         purgeline();
  581.         sendline(EOT);
  582.         fflush(stdout);
  583.         ++attempts;
  584.     }
  585.         while ((firstch=(readock(Rxtimeout, 1)) != ACK) && attempts < RETRYMAX);
  586.     if (attempts == RETRYMAX) {
  587.         logent("No ACK on EOT\n");
  588.         return ERROR;
  589.     }
  590.     else
  591.         return OK;
  592. }
  593.  
  594. wcputsec(buf, sectnum, cseclen)
  595. char *buf;
  596. int sectnum;
  597. int cseclen;    /* data length of this sector to send */
  598. {
  599.     register checksum, wcj;
  600.     register char *cp;
  601.     unsigned oldcrc;
  602.     int firstch;
  603.     int attempts;
  604.  
  605.     firstch=0;    /* part of logic to detect CAN CAN */
  606.  
  607.     if (Verbose>1)
  608.         fprintf(stderr, "\rSector %3d %2dk ", Totsecs, Totsecs/8 );
  609.     for (attempts=0; attempts <= RETRYMAX; attempts++) {
  610.         Lastrx= firstch;
  611.         sendline(cseclen==KSIZE?STX:SOH);
  612.         sendline(sectnum);
  613.         sendline(-sectnum -1);
  614.         oldcrc=checksum=0;
  615.         for (wcj=cseclen,cp=buf; --wcj>=0; ) {
  616.             sendline(*cp);
  617.             oldcrc=updcrc((0377& *cp), oldcrc);
  618.             checksum += *cp++;
  619.         }
  620.         if (Crcflg) {
  621.             oldcrc=updcrc(0,updcrc(0,oldcrc));
  622.             sendline((int)oldcrc>>8);
  623.             sendline((int)oldcrc);
  624.         }
  625.         else
  626.             sendline(checksum);
  627.  
  628.         if (Optiong) {
  629.             firstsec = FALSE; return OK;
  630.         }
  631.         firstch = readock(Rxtimeout, (Noeofseen&§num) ? 2:1);
  632. gotnak:
  633.         switch (firstch) {
  634.         case CAN:
  635.             if(Lastrx == CAN) {
  636. cancan:
  637.                 logent("Cancelled\n");  return ERROR;
  638.             }
  639.             break;
  640.         case TIMEOUT:
  641.             logent("Timeout on sector ACK\n"); continue;
  642.         case WANTCRC:
  643.             if (firstsec)
  644.                 Crcflg = TRUE;
  645.         case NAK:
  646.             logent("NAK on sector\n"); continue;
  647.         case ACK: 
  648.             firstsec=FALSE;
  649.             Totsecs += (cseclen>>7);
  650.             return OK;
  651.         case ERROR:
  652.             logent("Got burst for sector ACK\n"); break;
  653.         default:
  654.             logent("Got %02x for sector ACK\n", firstch); break;
  655.         }
  656.         for (;;) {
  657.             Lastrx = firstch;
  658.             if ((firstch = readock(Rxtimeout, 2)) == TIMEOUT)
  659.                 break;
  660.             if (firstch == NAK || firstch == WANTCRC)
  661.                 goto gotnak;
  662.             if (firstch == CAN && Lastrx == CAN)
  663.                 goto cancan;
  664.         }
  665.     }
  666.     logent("Retry Count Exceeded\n");
  667.     return ERROR;
  668. }
  669.  
  670. /* fill buf with count chars padding with ^Z for CPM */
  671. filbuf(buf, count)
  672. register char *buf;
  673. {
  674.     register c, m;
  675.  
  676.     if ( !Ascii) {
  677.         m = read(fileno(in), buf, count);
  678.         if (m <= 0)
  679.             return 0;
  680.         while (m < count)
  681.             buf[m++] = 032;
  682.         return count;
  683.     }
  684.     m=count;
  685.     if (Lfseen) {
  686.         *buf++ = 012; --m; Lfseen = 0;
  687.     }
  688.     while ((c=getc(in))!=EOF) {
  689.         if (c == 012) {
  690.             *buf++ = 015;
  691.             if (--m == 0) {
  692.                 Lfseen = TRUE; break;
  693.             }
  694.         }
  695.         *buf++ =c;
  696.         if (--m == 0)
  697.             break;
  698.     }
  699.     if (m==count)
  700.         return 0;
  701.     else
  702.         while (--m>=0)
  703.             *buf++ = CPMEOF;
  704.     return count;
  705. }
  706. /* fill buf with count chars */
  707. zfilbuf(buf, count)
  708. register char *buf;
  709. {
  710.     register c, m;
  711.  
  712.     m=count;
  713.     while ((c=getc(in))!=EOF) {
  714.         *buf++ =c;
  715.         if (--m == 0)
  716.             break;
  717.     }
  718.     return (count - m);
  719. }
  720.  
  721. /* VARARGS1 */
  722. vfile(f, a, b, c)
  723. register char *f;
  724. {
  725.     if (Verbose > 1) {
  726.         fprintf(stderr, f, a, b, c);
  727.         fprintf(stderr, "\n");
  728.     }
  729. }
  730.  
  731.  
  732. alrm()
  733. {
  734.     longjmp(tohere, -1);
  735. }
  736.  
  737.  
  738. /*
  739.  * readock(timeout, count) reads character(s) from file descriptor 0
  740.  *  (1 <= count <= 3)
  741.  * it attempts to read count characters. If it gets more than one,
  742.  * it is an error unless all are CAN
  743.  * (otherwise, only normal response is ACK, CAN, or C)
  744.  *  Only looks for one if Optiong, which signifies cbreak, not raw input
  745.  *
  746.  * timeout is in tenths of seconds
  747.  */
  748. readock(timeout, count)
  749. {
  750.     register int c;
  751.     static char byt[5];
  752.  
  753.     if (Optiong)
  754.         count = 1;    /* Special hack for cbreak */
  755.  
  756.     fflush(stdout);
  757.     if (setjmp(tohere)) {
  758.         logent("TIMEOUT\n");
  759.         return TIMEOUT;
  760.     }
  761.     c = timeout/10;
  762.     if (c<2)
  763.         c=2;
  764.     if (Verbose>3) {
  765.         fprintf(stderr, "Timeout=%d Calling alarm(%d) ", timeout, c);
  766.         byt[1] = 0;
  767.     }
  768.     signal(SIGALRM, alrm); alarm(c);
  769. #ifdef ONEREAD
  770.     c=read(iofd, byt, 1);        /* regulus raw read is unique */
  771. #else
  772.     c=read(iofd, byt, count);
  773. #endif
  774.     alarm(0);
  775.     if (Verbose>5)
  776.         fprintf(stderr, "ret cnt=%d %x %x\n", c, byt[0], byt[1]);
  777.     if (c<1)
  778.         return TIMEOUT;
  779.     if (c==1)
  780.         return (byt[0]&0377);
  781.     else
  782.         while (c)
  783.             if (byt[--c] != CAN)
  784.                 return ERROR;
  785.     return CAN;
  786. }
  787. readline(n)
  788. {
  789.     return (readock(n, 1));
  790. }
  791.  
  792.  
  793. purgeline()
  794. {
  795. #ifdef USG
  796.     ioctl(iofd, TCFLSH, 0);
  797. #else
  798.     lseek(iofd, 0L, 2);
  799. #endif
  800. }
  801.  
  802.  
  803. /* send cancel string to get the other end to shut up */
  804. canit()
  805. {
  806.     static char canistr[] = {
  807.      24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
  808.     };
  809.  
  810.     printf(canistr);
  811.     fflush(stdout);
  812. }
  813.  
  814. /*VARARGS1*/
  815. logent(a, b, c)
  816. char *a, *b, *c;
  817. {
  818.     if(Verbose)
  819.         fprintf(stderr, a, b, c);
  820. }
  821.  
  822. /*
  823.  * return 1 iff stdout and stderr are different devices
  824.  *  indicating this program operating with a modem on a
  825.  *  different line
  826.  */
  827. from_cu()
  828. {
  829.     struct stat a, b;
  830.     fstat(1, &a); fstat(2, &b);
  831.     return (a.st_rdev != b.st_rdev);
  832. }
  833.  
  834. /*
  835.  * substr(string, token) searches for token in string s
  836.  * returns pointer to token within string if found, NULL otherwise
  837.  */
  838. char *
  839. substr(s, t)
  840. register char *s,*t;
  841. {
  842.     register char *ss,*tt;
  843.     /* search for first char of token */
  844.     for (ss=s; *s; s++)
  845.         if (*s == *t)
  846.             /* compare token with substring */
  847.             for (ss=s,tt=t; ;) {
  848.                 if (*tt == 0)
  849.                     return s;
  850.                 if (*ss++ != *tt++)
  851.                     break;
  852.             }
  853.     return NULL;
  854. }
  855.  
  856. char *babble[] = {
  857.     "Send file(s) with ZMODEM/YMODEM/XMODEM Protocol",
  858.     "    (Y) = Option applies to YMODEM only",
  859.     "    (Z) = Option applies to ZMODEM only",
  860.     "Usage:    sz [-12+adefkLlNnquvXy] [-] file ...",
  861.     "    sz [-1eqv] -c COMMAND",
  862.     "    1 Use stdout for modem input",
  863. #ifdef CSTOPB
  864.     "    2 Use 2 stop bits",
  865. #endif
  866.     "    + Append to existing destination file (Z)",
  867.     "    a (ASCII) change NL to CR/LF",
  868.     "    c send COMMAND (Z)",
  869.     "    d Change '.' to '/' in pathnames (Y/Z)",
  870.     "    e Escape all control characters (Z)",
  871.     "    f send Full pathname (Y/Z)",
  872.     "    i send COMMAND, ack Immediately (Z)",
  873.     "    k Send 1024 byte packets (Y)",
  874.     "    L N Limit subpacket length to N bytes (Z)",
  875.     "    l N Limit frame length to N bytes (l>=L) (Z)",
  876.     "    n send file if source Newer or longer (Z)",
  877.     "    N send file if source different length or date (Z)",
  878.     "    p Protect existing destination file (Z)",
  879.     "    r Resume/Recover interrupted file transfer (Z)",
  880.     "    q Quiet (no progress reports)",
  881.     "    u Unlink file after transmission",
  882.     "    v Verbose - debugging information",
  883.     "    X XMODEM protocol - send no pathnames",
  884.     "    y Yes, overwrite existing file (Z)",
  885.     "- as pathname sends standard input as sPID.sz or environment ONAME",
  886.     ""
  887. };
  888.  
  889. usage()
  890. {
  891.     char **pp;
  892.  
  893.     for (pp=babble; **pp; ++pp)
  894.         fprintf(stderr, "%s\n", *pp);
  895.     fprintf(stderr, "%s for %s by Chuck Forsberg  ", VERSION, OS);
  896.     exit(1);
  897. }
  898.  
  899. /*
  900.  * Get the receiver's init parameters
  901.  */
  902. getzrxinit()
  903. {
  904.     register n;
  905.     struct stat f;
  906.  
  907.     for (n=10; --n>=0; ) {
  908.         
  909.         switch (zgethdr(Rxhdr, 1)) {
  910.         case ZCHALLENGE:    /* Echo receiver's challenge numbr */
  911.             stohdr(Rxpos);
  912.             zshhdr(ZACK, Txhdr);
  913.             continue;
  914.         case ZCOMMAND:        /* They didn't see out ZRQINIT */
  915.             stohdr(0L);
  916.             zshhdr(ZRQINIT, Txhdr);
  917.             continue;
  918.         case ZRINIT:
  919.             Rxflags = 0377 & Rxhdr[ZF0];
  920.             Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8);
  921.             vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen);
  922.             if ( !Fromcu)
  923.                 signal(SIGINT, SIG_IGN);
  924. #ifndef READCHECK
  925. #ifdef USG
  926.             mode(2);    /* Set cbreak, XON/XOFF, etc. */
  927. #else
  928.             /* Use 1024 byte frames if no sample/interrupt */
  929.             if (Rxbuflen < 32 || Rxbuflen > 1024) {
  930.                 Rxbuflen = 1024;
  931.                 vfile("Rxbuflen=%d", Rxbuflen);
  932.             }
  933. #endif
  934. #endif
  935.             /* Override to force shorter frame length */
  936.             if (Rxbuflen && (Rxbuflen>Tframlen) && (Tframlen>=32))
  937.                 Rxbuflen = Tframlen;
  938.             if ( !Rxbuflen && (Tframlen>=32) && (Tframlen<=1024))
  939.                 Rxbuflen = Tframlen;
  940.             vfile("Rxbuflen=%d", Rxbuflen);
  941.  
  942.             /* If using a pipe for testing set lower buf len */
  943.             fstat(iofd, &f);
  944.             if ((f.st_mode & S_IFMT) != S_IFCHR
  945.               && (Rxbuflen == 0 || Rxbuflen > 4096))
  946.                 Rxbuflen = 4096;
  947.             /*
  948.              * If input is not a regular file, force ACK's each 1024
  949.              *  (A smarter strategey could be used here ...)
  950.              */
  951.             fstat(fileno(in), &f);
  952.             if (((f.st_mode & S_IFMT) != S_IFREG)
  953.               && (Rxbuflen == 0 || Rxbuflen > 1024))
  954.                 Rxbuflen = 1024;
  955.             vfile("Rxbuflen=%d", Rxbuflen);
  956.  
  957.             return (sendzsinit());
  958.         case ZCAN:
  959.         case TIMEOUT:
  960.             return ERROR;
  961.         case ZRQINIT:
  962.             if (Rxhdr[ZF0] == ZCOMMAND)
  963.                 continue;
  964.         default:
  965.             zshhdr(ZNAK, Txhdr);
  966.             continue;
  967.         }
  968.     }
  969.     return ERROR;
  970. }
  971.  
  972. /* Send send-init information */
  973. sendzsinit()
  974. {
  975.     register c;
  976.     register errors;
  977.  
  978.     if (Myattn[0] == '\0')
  979.         return OK;
  980.     errors = 0;
  981.     for (;;) {
  982.         stohdr(0L);
  983.         zsbhdr(ZSINIT, Txhdr);
  984.         zsdata(Myattn, 1+strlen(Myattn), ZCRCW);
  985.         c = zgethdr(Rxhdr, 1);
  986.         switch (c) {
  987.         case ZCAN:
  988.             return ERROR;
  989.         case ZACK:
  990.             return OK;
  991.         default:
  992.             if (++errors > 9)
  993.                 return ERROR;
  994.             continue;
  995.         }
  996.     }
  997. }
  998.  
  999. /* Send file name and related info */
  1000. zsendfile(buf, blen)
  1001. char *buf;
  1002. {
  1003.     register c;
  1004.  
  1005.     for (;;) {
  1006.         Txhdr[ZF0] = Lzconv;    /* file conversion request */
  1007.         Txhdr[ZF1] = Lzmanag;    /* file management request */
  1008.         Txhdr[ZF2] = Lztrans;    /* file transport request */
  1009.         Txhdr[ZF3] = 0;
  1010.         zsbhdr(ZFILE, Txhdr);
  1011.         zsdata(buf, blen, ZCRCW);
  1012. again:
  1013.         c = zgethdr(Rxhdr, 1);
  1014.         switch (c) {
  1015.         case ZRINIT:
  1016.             goto again;
  1017.         case ZCAN:
  1018.         case TIMEOUT:
  1019.         case ZABORT:
  1020.         case ZFIN:
  1021.             return ERROR;
  1022.         case ZSKIP:
  1023.             fclose(in); return c;
  1024.         case ZRPOS:
  1025.             fseek(in, Rxpos, 0);
  1026.             Txpos = Rxpos; Lastc = -1; Dontread = FALSE;
  1027.             return zsendfdata();
  1028.         case ERROR:
  1029.         default:
  1030.             continue;
  1031.         }
  1032.     }
  1033. }
  1034.  
  1035. /* Send the data in the file */
  1036. zsendfdata()
  1037. {
  1038.     register c, e;
  1039.     register newcnt;
  1040.     register long tcount = 0;
  1041.     static int tleft = 6;    /* Counter for test mode */
  1042.  
  1043.     if (Baudrate > 300)
  1044.         blklen = 256;
  1045.     if (Baudrate > 2400)
  1046.         blklen = KSIZE;
  1047.     if (Rxbuflen && blklen>Rxbuflen)
  1048.         blklen = Rxbuflen;
  1049.     if (blkopt && blklen > blkopt)
  1050.         blklen = blkopt;
  1051.     vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen);
  1052. somemore:
  1053.     if (setjmp(intrjmp)) {
  1054. waitack:
  1055.         c = getinsync();
  1056.         switch (c) {
  1057.         default:
  1058.         case ZCAN:
  1059.             fclose(in);
  1060.             return ERROR;
  1061.         case ZSKIP:
  1062.             fclose(in);
  1063.             return c;
  1064.         case ZACK:
  1065.         case ZRPOS:
  1066.             break;
  1067.         case ZRINIT:
  1068.             return OK;
  1069.         }
  1070. #ifdef READCHECK
  1071.         /*
  1072.          * If the reverse channel can be tested for data,
  1073.          *  this logic may be used to detect error packets
  1074.          *  sent by the receiver, in place of setjmp/longjmp
  1075.          *  rdchk(fdes) returns non 0 if a character is available
  1076.          */
  1077.         while (rdchk(iofd)) {
  1078. #ifdef SVR2
  1079.             switch (checked)
  1080. #else
  1081.             switch (readline(1))
  1082. #endif
  1083.             {
  1084.             case CAN:
  1085.             case ZPAD:
  1086.                 goto waitack;
  1087.             }
  1088.         }
  1089. #endif
  1090.     }
  1091.  
  1092.     if ( !Fromcu)
  1093.         signal(SIGINT, onintr);
  1094.     newcnt = Rxbuflen;
  1095.     stohdr(Txpos);
  1096.     zsbhdr(ZDATA, Txhdr);
  1097.  
  1098.     /*
  1099.      * Special testing mode.  This should force receiver to Attn,ZRPOS
  1100.      *  many times.  Each time the signal should be caught, causing the
  1101.      *  file to be started over from the beginning.
  1102.      */
  1103.     if (Testattn) {
  1104.         if ( --tleft)
  1105.             while (tcount < 20000) {
  1106.                 printf(qbf); fflush(stdout);
  1107.                 tcount += strlen(qbf);
  1108. #ifdef READCHECK
  1109.                 while (rdchk(iofd)) {
  1110. #ifdef SVR2
  1111.                     switch (checked)
  1112. #else
  1113.                     switch (readline(1))
  1114. #endif
  1115.                     {
  1116.                     case CAN:
  1117.                     case ZPAD:
  1118. #ifdef TCFLSH
  1119.                         ioctl(iofd, TCFLSH, 1);
  1120. #endif
  1121.                         goto waitack;
  1122.                     }
  1123.                 }
  1124. #endif
  1125.             }
  1126.         signal(SIGINT, SIG_IGN); canit();
  1127.         sleep(3); purgeline(); mode(0);
  1128.         printf("\nsz: Tcount = %ld\n", tcount);
  1129.         if (tleft) {
  1130.             printf("ERROR: Interrupts Not Caught\n");
  1131.             exit(1);
  1132.         }
  1133.         exit(0);
  1134.     }
  1135.  
  1136.     do {
  1137.         if (Dontread) {
  1138.             c = Lastc;
  1139.         } else {
  1140.             c = zfilbuf(txbuf, blklen);
  1141.             Lastread = Txpos;  Lastc = c;
  1142.         }
  1143.         if (Verbose > 10)
  1144.             vfile("Dontread=%d c=%d", Dontread, c);
  1145.         Dontread = FALSE;
  1146.         if (c < blklen)
  1147.             e = ZCRCE;
  1148.         else if (Rxbuflen && (newcnt -= c) <= 0)
  1149.             e = ZCRCW;
  1150.         else
  1151.             e = ZCRCG;
  1152.         zsdata(txbuf, c, e);
  1153.         Txpos += c;
  1154.         if (e == ZCRCW)
  1155.             goto waitack;
  1156. #ifdef READCHECK
  1157.         /*
  1158.          * If the reverse channel can be tested for data,
  1159.          *  this logic may be used to detect error packets
  1160.          *  sent by the receiver, in place of setjmp/longjmp
  1161.          *  rdchk(fdes) returns non 0 if a character is available
  1162.          */
  1163.         fflush(stdout);
  1164.         while (rdchk(iofd)) {
  1165. #ifdef SVR2
  1166.             switch (checked)
  1167. #else
  1168.             switch (readline(1))
  1169. #endif
  1170.             {
  1171.             case CAN:
  1172.             case ZPAD:
  1173. #ifdef TCFLSH
  1174.                 ioctl(iofd, TCFLSH, 1);
  1175. #endif
  1176.                 /* zcrce - dinna wanna start a ping-pong game */
  1177.                 zsdata(txbuf, 0, ZCRCE);
  1178.                 goto waitack;
  1179.             }
  1180.         }
  1181. #endif
  1182.     } while (c == blklen);
  1183.     if ( !Fromcu)
  1184.         signal(SIGINT, SIG_IGN);
  1185.  
  1186.     for (;;) {
  1187.         stohdr(Txpos);
  1188.         zsbhdr(ZEOF, Txhdr);
  1189.         switch (getinsync()) {
  1190.         case ZACK:
  1191.             continue;
  1192.         case ZRPOS:
  1193.             goto somemore;
  1194.         case ZRINIT:
  1195.             return OK;
  1196.         case ZSKIP:
  1197.             fclose(in);
  1198.             return c;
  1199.         default:
  1200.             fclose(in);
  1201.             return ERROR;
  1202.         }
  1203.     }
  1204. }
  1205.  
  1206. /*
  1207.  * Respond to receiver's complaint, get back in sync with receiver
  1208.  */
  1209. getinsync()
  1210. {
  1211.     register c;
  1212.  
  1213.     for (;;) {
  1214.         if (Testattn) {
  1215.             printf("\r\n\n\n***** Signal Caught *****\r\n");
  1216.             Rxpos = 0; c = ZRPOS;
  1217.         } else
  1218.             c = zgethdr(Rxhdr, 0);
  1219.         switch (c) {
  1220.         case ZCAN:
  1221.         case ZABORT:
  1222.         case ZFIN:
  1223.         case TIMEOUT:
  1224.             return ERROR;
  1225.         case ZRPOS:
  1226.             if (Lastc >= 0 && Lastread == Rxpos) {
  1227.                 Dontread = TRUE;
  1228.             } else {
  1229.                 clearerr(in);    /* In case file EOF seen */
  1230.                 fseek(in, Rxpos, 0);
  1231.             }
  1232.             Txpos = Rxpos;
  1233.             return c;
  1234.         case ZACK:
  1235.             return c;
  1236.         case ZRINIT:
  1237.         case ZSKIP:
  1238.             fclose(in);
  1239.             return c;
  1240.         case ERROR:
  1241.         default:
  1242.             zsbhdr(ZNAK, Txhdr);
  1243.             continue;
  1244.         }
  1245.     }
  1246. }
  1247. /* Say "bibi" to the receiver, try to do it cleanly */
  1248. saybibi()
  1249. {
  1250.     for (;;) {
  1251.         stohdr(0L);
  1252.         zsbhdr(ZFIN, Txhdr);
  1253.         switch (zgethdr(Rxhdr, 0)) {
  1254.         case ZFIN:
  1255.             sendline('O'); sendline('O'); flushmo();
  1256.         case ZCAN:
  1257.         case TIMEOUT:
  1258.             return;
  1259.         }
  1260.     }
  1261. }
  1262.  
  1263. /* Local screen character display function */
  1264. bttyout(c)
  1265. {
  1266.     if (Verbose)
  1267.         putc(c, stderr);
  1268. }
  1269.  
  1270. /* Send command and related info */
  1271. zsendcmd(buf, blen)
  1272. char *buf;
  1273. {
  1274.     register c, errors;
  1275.     long cmdnum;
  1276.  
  1277.     cmdnum = getpid();
  1278.     errors = 0;
  1279.     for (;;) {
  1280.         stohdr(cmdnum);
  1281.         Txhdr[ZF0] = Cmdack1;
  1282.         zsbhdr(ZCOMMAND, Txhdr);
  1283.         zsdata(buf, blen, ZCRCW);
  1284. listen:
  1285.         Rxtimeout = 100;        /* Ten second wait for resp. */
  1286.         c = zgethdr(Rxhdr, 1);
  1287.  
  1288.         switch (c) {
  1289.         case ZRINIT:
  1290.             continue;
  1291.         case ERROR:
  1292.         case TIMEOUT:
  1293.             if (++errors > Cmdtries)
  1294.                 return ERROR;
  1295.             continue;
  1296.         case ZCAN:
  1297.         case ZABORT:
  1298.         case ZFIN:
  1299.         case ZSKIP:
  1300.         case ZRPOS:
  1301.             return ERROR;
  1302.         default:
  1303.             if (++errors > 10)
  1304.                 return ERROR;
  1305.             continue;
  1306.         case ZCOMPL:
  1307.             Exitcode = Rxpos;
  1308.             saybibi();
  1309.             return OK;
  1310.         case ZRQINIT:
  1311.             vfile("******** RZ *******");
  1312.             system("rz");
  1313.             vfile("******** SZ *******");
  1314.             goto listen;
  1315.         }
  1316.     }
  1317. }
  1318.  
  1319. /*
  1320.  * If called as sb use YMODEM protocol
  1321.  */
  1322. chkinvok(s)
  1323. char *s;
  1324. {
  1325.     register char *p;
  1326.  
  1327.     p = s;
  1328.     while (*p == '-')
  1329.         s = ++p;
  1330.     while (*p)
  1331.         if (*p++ == '/')
  1332.             s = p;
  1333.     if (*s == 'v') {
  1334.         Verbose=1; ++s;
  1335.     }
  1336.     Progname = s;
  1337.     if (s[0]=='s' && s[1]=='b') {
  1338.         Nozmodem = TRUE; blklen=KSIZE;
  1339.     }
  1340. }
  1341.  
  1342.